/*
 * Confabulate: http://code.google.com/p/confabulate/
 * @author: Sam Keen @samkeen sam/at\releasethecodes.com
 * MPL 1.1 : http://www.mozilla.org/MPL/MPL-1.1.html  (http://www.mozilla.org/MPL/MPL-1.1-annotated.html)
 * Requrements
 *  - jquery 1.3.x
 *
 *  - tested browsers
 *	- FF 3.5.x	_Y_
 *	- Safari 4.x	_N_
 *	- Opera 9.6.x	_N_
 */

var confab = {
    // the persistance server for the interests and markers
    rcrd_server : 'http://local.rcrd.com',
    // the current makers indexed by id (primary key)
    current_markers : {},
    // defined in this.click_draw
    click_coords:null,
    // folder where interest images would be stoed locally [defualt to store on rsrd_server
    interest_folder : '/interests',
    current_interest_id : null,
    // the canvas for the markers
    canvas_id : null,
    canvas_element : null,
    canv_context : null,
    // the left/top offset of the canvas on the page. Used to translate pointer clicks
    // to canvas clicks
    canvas_offset : {left:0,top:0},
    // take scrolling into account when translating window pointer position to
    // canvas pointer position.
    v_scroll_offset : 0,
    h_scroll_offset : 0,
    // flag used in dnd
    pointer_is_down : false,
    // the index of the marker in this.current_markers that the pointer is over (if any)
    over_marker_id : null,
    // the index of the marker in this.current_markers that we are dragging (if any)
    dragging_marker_id : null,

    canvas_highlight_color : 'orange',
    canvas_default_draw_color : 'black',
    canvas_default_marker_size : '30px',

    //dialogs: {marker:,edit_marker:null},

    init:function(canvas_id, current_interest_id) {
	
	$RTC_marker_dialog.init({
	    form_input_ids:['marker_label','marker_desc','marker_size','marker_color'],
	    feedback_element_id:'validateTips',
	    dialog_id:'marker_dialog',
	    close_callback:this.handle_marker_dialog_close
	});
	this.current_interest_id = typeof current_interest_id === "undefined"?null:current_interest_id;
	$RTC_log.debug("current_interest_id["+this.current_interest_id+"]");
	this.canvas_id = canvas_id;
        // retrieves and sets the markers to this.current_markers
        this.populate_markers();
	
    },
    remove_marker : function(marker_id) {
	var that = this;
	this.over_marker_id = null;
        $.getJSON(
            that.rcrd_server+"/interest/"+this.current_interest_id+"/marker/"+marker_id+".json?_method=delete&jsoncallback=?",
            function(deleted_marker, text_status){
                $RTC_log.info( "Marker ["+deleted_marker+"] deleted:"+text_status);
                // repaint the sheet
		$('#marker_'+marker_id).remove();
                that.populate_markers();
        });
    },
    /*
     * @param {} marker
     */
    edit_marker : function(marker) {
	var that = this;
	// shift coordinates relative to 0,0 origin
        $.getJSON(
            that.rcrd_server+"/interest/"+this.current_interest_id+"/marker/"+marker.id+".json?_method=edit&jsoncallback=?",
            that.square_brackify_keys('marker', marker),
            function(edited_marker, text_status){
                $RTC_log.info( "Marker ["+edited_marker+"] updated:"+text_status);
                // repaint the sheet
                that.populate_markers();
        });
    },
    add_marker : function(marker) {
	var that = this;
        $.getJSON(
            that.rcrd_server+"/interest/"+this.current_interest_id+"/marker.json?_method=add&jsoncallback=?",
            that.square_brackify_keys('marker', marker),
            function(added_marker, text_status){
                $RTC_log.info( "Data Saved: "+added_marker);
		that.current_markers[added_marker[0].id] = added_marker[0];
		$('#marker_descriptions').append(that.marker_desc_list_element(added_marker[0]));
		// redraw the canvas
		that.draw_current_markers();
        });
    },
    click_draw:function(click_event) {
	this.click_coords = this.get_canvas_coords_clicked(click_event);
	$RTC_log.info('click recorded at: ['+this.click_coords.canvas_x+','+this.click_coords.canvas_y+']');
//      var coordinate_label = prompt('enter a label for this marker');
//	var coordinate_description = prompt('enter a label for this marker');
        this.open_dialog();
        
    },
    /**
     * This is handed to the $RTC_marker_dialog init config as close_callback:
     *
     * @param all_fields (object) Keys are 'marker_label','marker_desc','marker_size','marker_color'
     * @param delete_marker (boolean) true if user submitted the delete marker command
     *
     */
    handle_marker_dialog_close:function(all_fields, delete_marker) {
	$RTC_log.debug('all fields',all_fields);
	if(confab.click_coords && all_fields['marker_label']) { // ADD marker
	    confab.draw_marker({
		coordinate_label:all_fields['marker_label'],
		coordinate_description:all_fields['marker_desc'],
		marker_size: all_fields['marker_size'],
		marker_color: all_fields['marker_color'],
		coordinate_x:confab.click_coords.canvas_x,
		coordinate_y:confab.click_coords.canvas_y
	    }, 'add');
        } else if(confab.over_marker_id) { //EDIT marker
//	    var new_label = prompt('Edit this link (enter blank line to delete)',this.current_markers[this.over_marker_id].coordinate_label);
//	    if(new_label!=null) {
	    if(delete_marker) {
		confab.remove_marker(confab.over_marker_id);
	    } else if(all_fields['marker_label']) {
		confab.current_markers[confab.over_marker_id].coordinate_label = all_fields['marker_label'];
		confab.current_markers[confab.over_marker_id].coordinate_description = all_fields['marker_desc'];
		confab.current_markers[confab.over_marker_id].marker_size = all_fields['marker_size'];
		confab.current_markers[confab.over_marker_id].marker_color = all_fields['marker_color'];
		confab.draw_marker(confab.current_markers[confab.over_marker_id], 'edit');
	    }
	} else {
            $RTC_log.debug('draw marker canceled');
        }
    },
    open_dialog:function(dialog_state) {
	if(dialog_state) {
	    // show the Delete button
	    $('.ui-dialog-buttonpane button:last').show();
	    $('.ui-dialog-buttonpane button:first').html('Save');
	    var dialog_fields = {
		marker_label:dialog_state.coordinate_label,
		marker_desc:dialog_state.coordinate_description,
		marker_size:dialog_state.marker_size,
		marker_color:dialog_state.marker_color
	    }
	   $RTC_marker_dialog.set_field_values(dialog_fields);
	} else {
	    // Hide the Delete button
	    $('.ui-dialog-buttonpane button:first').html('Create Marker');
	    $('.ui-dialog-buttonpane button:last').hide();
	}
	$('#marker_dialog').dialog('open');
	
    },
    /*
     * translate the click on the screen coords relative to the canvas
     * origin.
     */
    get_canvas_coords_clicked : function(click_event) {
	if (typeof(click_event)=="undefined")click_event=event;
	x_scroll_offset = window.pageXOffset!=='undefined'?window.pageXOffset:0;
	y_scroll_offset = window.pageYOffset!=='undefined'?window.pageYOffset:0;
	return {
	    canvas_x:(click_event.clientX-this.canvas_offset.left+x_scroll_offset),
	    canvas_y:(click_event.clientY-this.canvas_offset.top+y_scroll_offset)
	};
    },
    /*
     * determine the offset on the canvas element relative to the page.
     */
    calc_canvas_element_offset : function(){
	this.canvas_offset = $('#'+this.canvas_id).offset();
	this.canvas_offset.top = Math.floor(this.canvas_offset.top);
	this.canvas_offset.left = Math.floor(this.canvas_offset.left);
	$RTC_log.debug("canvas offset Top["+this.canvas_offset.top+"] Left["+this.canvas_offset.left+"]");
    },
    draw_current_markers:function() {
	this.canv_context.clearRect(0,0,this.canvas_element.width,this.canvas_element.height);
        for(var key in this.current_markers) {
	    if (this.current_markers.hasOwnProperty(key) ) {
		this.draw_marker(this.current_markers[key], 'view');
	    }
        }
    },
    paint_marker : function(marker, config) {
	var color = this.canvas_default_draw_color;
	if(typeof(config)!='undefined'&&config.hasOwnProperty('override_color')) {
	    color = config.override_color;
	} else if(marker.marker_color) {
	    color = marker.marker_color;
	}
	var size = marker.marker_size?marker.marker_size:this.canvas_default_marker_size
	size = size.match(/^\d+$/)?size+'px':size;
	var marker_center_values = {};
	marker_center_values.x = (Number(marker.coordinate_x)-9);
	marker_center_values.y = (Number(marker.coordinate_y)-13);
	this.canv_context.fillStyle = color;
	this.canv_context.textBaseline = "top";
	this.canv_context.font = size+" Serif";
	this.canv_context.fillText(marker.coordinate_label, marker_center_values.x, marker_center_values.y);
	$RTC_log.debug("MEASURE TEXT:", this.canv_context.measureText(marker.coordinate_label));
	return marker_center_values;
    },
    /*
     * @param {} marker {coordinate_label:_,coordinate_x:_,coordinate_y:_,}
     * @param string mode add | edit | view
     */
    draw_marker : function(marker, mode) {
	this.click_coords = null;
        var marker_bounds = {};
	var override_config = (mode=='add'||mode=='edit')?{override_color:'red'}:{};
	if(mode=='add'||mode=='edit') {
	    // clear the canvas
	    this.canv_context.clearRect(0,0,this.canvas_element.width,this.canvas_element.height);
	}
	var marker_center_values = this.paint_marker(marker, override_config);
//	if(mode=='view') {
//	    this.canv_context.strokeStyle = "white";
//	    this.canv_context.strokeText(marker.coordinate_label, click_x_persist_value, click_y_persist_value);
//	}

	if(mode=='add'||mode=='edit') {
	    marker_bounds = $RTC_canvas_util.find_bounds(this.canvas_element,{red:255,green:null,blue:null});
	    if($RTC_log.debug_on){
		// place a green ref dot of where the mouse click was recorded
		this.canv_context.fillStyle = "green";
		this.canv_context.fillRect(marker.coordinate_x, marker.coordinate_y,2,2);
		$RTC_log.debug(marker_bounds);
		$RTC_log.debug('x:'+marker.coordinate_x+' y:'+marker.coordinate_y);
		$RTC_log.debug(this.debug_bounding_box(marker_bounds));
	    }
	    if( ! marker_bounds.found) {
		$RTC_log.error("unable to determine bounds of last marker.  It will not be persisted")
	    } else {
		//@todo need to clear first
		this.canv_context.fillStyle = marker.marker_color?marker.marker_color:this.canvas_default_draw_color;
		this.canv_context.fillText(marker.coordinate_label, marker_center_values.x, marker_center_values.y);
		$RTC_log.debug("MEASURE TEXT:", this.canv_context.measureText(marker.coordinate_label));
		marker.bound_north = marker_bounds.north;
		marker.bound_south = marker_bounds.south;
		marker.bound_west = marker_bounds.west;
		marker.bound_east = marker_bounds.east;
		if(mode=='add'){
		    this.add_marker(marker);
		} else { // mode==edit
		    this.edit_marker(marker);
		}
	    }

	    
	}
	if(mode!=='add') {
	    if($('#marker_'+marker.id).length>0) {
		$('#marker_'+marker.id).replaceWith(this.marker_desc_list_element(marker));
	    } else {
		$('#marker_descriptions').append(this.marker_desc_list_element(marker));
	    }
	}
    },
    marker_desc_list_element : function(marker) {
	var that = this;
	return $('<li id="marker_'+marker.id+'">'+marker.coordinate_label+'<p>'+marker.coordinate_description+'</p></li>')
	    .mouseover(function(){
		var marker_id = $(this).attr('id').match(/\d+$/);
		$RTC_log.debug("Ready to highlight marker:",that.current_markers[marker_id]);
		that.paint_marker(that.current_markers[marker_id],{override_color: that.canvas_highlight_color});
	    })
	    .mouseout(function(){
		var marker_id = $(this).attr('id').match(/\d+$/);
		that.paint_marker(that.current_markers[marker_id]);
	    });
    },
    populate_markers : function() {
	var that = this;
	if(this.current_interest_id!==null) {
	    $.getJSON(that.rcrd_server+"/interest/"+this.current_interest_id+"/marker?_method=get&jsoncallback=?",
	    function(interest_w_markers, text_status){
		var image_path = (interest_w_markers['interest']['image_uri'].match(/^https?:\/\//) )?interest_w_markers['interest']['image_uri']:this.interest_folder+'/'+interest_w_markers['interest']['image_uri'];
		var image_height = null;
		var image_width = null;
		// place the title above the canvas
		$('#title').html(interest_w_markers['interest']['name']);
		that.current_markers = {};
		for(marker_key in interest_w_markers['marker']) {
		    that.current_markers[interest_w_markers['marker'][marker_key].id] = interest_w_markers['marker'][marker_key];
		}
		$RTC_log.debug("Backgroung Image Path:"+image_path);
		$('#source_image').attr('src', image_path);
		/*
		 * once the image loads, get its size and use that to create the
		 * canvas tag at the appropriate size.
		 */
		$('#source_image').load(function() {
		    $('#source_image').removeAttr("width").removeAttr("height");
		    image_height = $('#source_image').height();
		    image_width = $('#source_image').width();
		    $RTC_log.debug("Image Height:"+image_height);
		    $RTC_log.debug("Image Width:"+image_width);
		    // remove any previous canvas
		    if ( $("#"+that.canvas_id).length > 0 ) {
			$("#"+that.canvas_id).remove();
		    }
		    $('#content').prepend('<canvas id="'+that.canvas_id+'" width="'+image_width+'" height="'+image_height+'" style="border:solid thin black;"></canvas>');
		    // set the css image background of the canvas
		    $('#'+that.canvas_id).css('background-image', "url('"+image_path+"')" );
		    that.calc_canvas_element_offset();
		    that.canvas_element = document.getElementById(that.canvas_id);
		    that.canv_context = that.canvas_element.getContext('2d');
		    // attach handlers
		    that.canvas_element.onmouseup = function(event) {
			that.pointer_up_handler(event);
		    }
		    that.canvas_element.onmouseout = function(event) {
			that.pointer_out_handler(event);
		    }
		    that.canvas_element.onmousemove = function(event) {
			that.pointer_move_handler(event);
		    }
		    that.canvas_element.onmousedown = function(event) {
			that.pointer_down_handler(event);
		    }
		    that.draw_current_markers();
		    that.draw_image_at(20, 20);
		});
	    });
	} else {
	    $RTC_log.error("making call to populate_markers but current_interest_id is not set. Aborting call");
	}
    },
    track_pointer_over_state : function(pointer_x, pointer_y) {
        // if we are over a marker watch that we don't leave it'
        if(this.over_marker_id != null) {
	    if(this.pointer_left_marker_bounds(pointer_x, pointer_y)) {
		$('#marker_'+this.over_marker_id).css('background-color', '#eee');
                this.over_marker_id = null;
	    }
        } else { // for each marker see if we are over it
            this.over_marker_id = this.pointer_over_marker_id(pointer_x, pointer_y);
        }
        if(this.over_marker_id!=null) {
	    $('#marker_'+this.over_marker_id).css('background-color', this.canvas_highlight_color);
	    $RTC_log.debug('Over Marker',this.current_markers[this.over_marker_id]);
        }
    },
    pointer_left_marker_bounds : function(pointer_x, pointer_y) {
	var over_marker = this.current_markers[this.over_marker_id];
	// shift the screen x/y to the relative to canvas x/y
	pointer_x = pointer_x - this.canvas_offset.left;
	pointer_y = pointer_y - this.canvas_offset.top;
	return	pointer_y < over_marker.bound_north
	    ||  pointer_y > over_marker.bound_south
	    ||  pointer_x < over_marker.bound_west
	    ||  pointer_x > over_marker.bound_east;
    },
    pointer_over_marker_id : function(pointer_x, pointer_y) {
	var over_marker = null;
	// shift the screen x/y to the relative to canvas x/y
	pointer_x = pointer_x - this.canvas_offset.left;
	pointer_y = pointer_y - this.canvas_offset.top;
	for(key in this.current_markers) {
	    if(     pointer_y > this.current_markers[key].bound_north
		&&  pointer_y < this.current_markers[key].bound_south
		&&  pointer_x > this.current_markers[key].bound_west
		&&  pointer_x < this.current_markers[key].bound_east) {
		over_marker = this.current_markers[key];
		$RTC_canvas_util.debug_highlight(this.canv_context, over_marker);
		break;
	    }
	}
	return over_marker!==null?over_marker.id:null;
    },
    set_sheet_background : function(image_file) {
	if(image_file.match(/^https?:\/\//) ) {
	    $('#'+this.canvas_id).css('background-image', "url('"+image_file+"')" );
	} else {
	    $('#'+this.canvas_id).css('background-image', "url('"+this.interest_folder+'/'+image_file+"')" );
	}
    },
    square_brackify_keys : function(key_name, obj) {
	var brackified = {};
	for(key in obj) {
	    brackified[key_name+'['+key+']'] = obj[key];
	}
	return brackified;
    },
    debug_bounding_box:function(bounding_box) {
        if($RTC_log.debug_on) {
            console.debug('boundign box corners:');
            console.debug(bounding_box.west+','+bounding_box.north+'-----'+bounding_box.east+','+bounding_box.north);
            console.debug('|             |');
            console.debug('|             |');
            console.debug('|             |');
            console.debug(bounding_box.west+','+bounding_box.south+'-----'+bounding_box.east+','+bounding_box.south);
        }
    },
    pointer_move_handler : function(event) {
	if(this.pointer_is_down && !this.dragging_marker_id) {
	    $RTC_log.debug("MOVE START, dragging Marker:", this.current_markers[this.over_marker_id]);
	    this.dragging_marker_id = this.over_marker_id;
	}
	this.track_pointer_over_state(event.pageX,event.pageY);
	var canvas_coords = null;
	if(this.dragging_marker_id) {
	    canvas_coords = this.get_canvas_coords_clicked(event);
	    this.current_markers[this.dragging_marker_id].coordinate_x = canvas_coords.canvas_x;
	    this.current_markers[this.dragging_marker_id].coordinate_y = canvas_coords.canvas_y;
	    this.draw_current_markers();
	}
    },
    pointer_up_handler : function(event) {
	this.pointer_is_down = false;
	if(this.dragging_marker_id) { // finishing a click and drag
	    $RTC_log.debug('DRAG STOP:, this.dragging_marker_id:', this.dragging_marker_id);
	    this.draw_marker(this.current_markers[this.dragging_marker_id],'edit');
	    this.dragging_marker_id = null;
	} else {// just a quick click down/up so we are creating a new marker or editing an existing one
	    if(this.over_marker_id!=null) {
		$RTC_log.debug('click over: "'+this.current_markers[this.over_marker_id].coordinate_label+'"');
		this.open_dialog(this.current_markers[this.over_marker_id]);
//		var new_label = prompt('Edit this link (enter blank line to delete)',this.current_markers[this.over_marker_id].coordinate_label);
//		if(new_label!=null) {
//		    if(new_label=='') {
//			this.remove_marker(this.over_marker_id);
//		    } else {
//			this.current_markers[this.over_marker_id].coordinate_label = new_label;
//			this.draw_marker(this.current_markers[this.over_marker_id], 'edit');
//		    }
//		}
	    } else { // draw new marker
		this.click_draw(event);
	    }
	}
	$RTC_log.debug('Pointer Up Fired:', event);
    },
    pointer_down_handler : function(event) {
	this.pointer_is_down = true;
    },
    pointer_out_handler : function(event) {

    },


    draw_image_at : function(x,y) {
      var that = this;
      var img = new Image();
      img.src = 'img/person.png';
      img.onload = function(){
	that.canv_context.drawImage(img,x,y);
      }
    }


};
var $RTC_canvas_util = {
    /**
     *
     *
     */
    find_bounds:function(canvas_element, seeking_rgba) {
        $RTC_log.info('$RTC_canvas_util.find_bounds(seeking_rgba): seeking_rgba:',seeking_rgba);
	var coords = {north_bound: 99999,south_bound: -99999, west_bound: 99999, east_bound: -99999}
        var target_channels = {red:null,green:null,blue:null,alpha:null};
        var canvas_data = canvas_element.getContext('2d').getImageData(
	    0, 0, canvas_element.width, canvas_element.height
	);
	var bounds_found = false;
        for (var x = 0; x < canvas_data.width; x++) {
            for (var y = 0; y < canvas_data.height; y++) {
                var idx = (x + y * canvas_element.width) * 4;
                // The RGB values
                target_channels.red = canvas_data.data[idx + 0];
                target_channels.green = canvas_data.data[idx + 1];
                target_channels.blue = canvas_data.data[idx + 2];
                target_channels.alpha = canvas_data.data[idx + 3];
		if(target_channels.red==255&&target_channels.green==null&&target_channels.blue==null){$RTC_log.debug("ALPHA   ",target_channels.alpha)};
                /*
                 * examine each channel, if it is at the value we are seeking, update
                 * n,s,w,e
                 */
                for (var color_channel in target_channels) {
                    if(seeking_rgba[color_channel]!=undefined) {
                        if(target_channels[color_channel]==seeking_rgba[color_channel]) {
                            bounds_found = true;
                        } else {
                            bounds_found = false;
			    break;
                        }
                    }
                }
                if(bounds_found) {
                    $RTC_log.debug('found  @ x:'+x+', y:'+y);
                    coords.north_bound = Math.min(coords.north_bound,y);
                    coords.south_bound = Math.max(coords.south_bound,y);
                    coords.west_bound = Math.min(coords.west_bound,x);
                    coords.east_bound = Math.max(coords.east_bound,x);
                    bounds_found = false;
                }
            }
        }
        return coords.north_bound<99999
            ? {found:true,north:coords.north_bound,south:coords.south_bound,west:coords.west_bound,east:coords.east_bound}
            : {found:false,north:null,south:null,west:null,east:null};
    },
    debug_highlight : function(context, marker) {
	context.moveTo(marker.bound_west, marker.bound_north);
	context.lineTo(marker.bound_east, marker.bound_north);

	context.moveTo(marker.bound_east, marker.bound_north);
	context.lineTo(marker.bound_east, marker.bound_south);

	context.moveTo(marker.bound_east, marker.bound_south);
	context.lineTo(marker.bound_west, marker.bound_south);

	context.moveTo(marker.bound_west, marker.bound_south);
	context.lineTo(marker.bound_west, marker.bound_north);

	context.strokeStyle = "green";
	context.stroke();
    }
}
var edit_in_place = {
    /*
     * {id:...,target_url:...}
     */
    editable : function(editable_element_id) {
	$('#'+editable_element_id)
	    .append($('<a id="interest_edit_'+id+'" href="">edit</a>')
	    .click(function(){
		edit_in_place.input_element($(this).attr('id'), $(this).prev().html());
		return false;
	    })
	);
    },
    label_element : function(id, label) {
	return  $('<li class="editable" id="interest_edit_'+id+'"><a href="interest.html?id='+id+'">'+label+'</a></li>')
	    .append($('<a id="interest_edit_'+id+'" href="">edit</a>')
	    .click(function(){
		edit_in_place.input_element($(this).attr('id'), $(this).prev().html());
		return false;
	    })
	);
    },
    input_element : function(element_id, label) {
	var id = element_id.match(/\d+$/);
	$('#'+element_id).replaceWith($('<li  class="editable" id="'+element_id+'"></li>')
	    .append($('<input class="input_edit" rel="'+label+'" id="input_'+id+'" value="'+label+'" type="text"></input>')
	    .keyup(function(e) {
		if(e.keyCode == 13) {
		    edit_in_place.edit_interest($(this).attr('id').match(/\d+$/),$(this).val());
		}
	    })
	    )
	    .append($('<a class="cancel_edit" href="">cancel</a>')
		.click(function(){
		    $('#interest_edit_'+id).replaceWith(edit_in_place.label_element(id,label));
		    return false;
		})
	    )
	);
    },
    edit_interest : function(current_interest_id, new_name) {
	// shift coordinates relative to 0,0 origin
	$.getJSON(
	    confab.rcrd_server+"/interest/"+current_interest_id+".json?_method=edit&jsoncallback=?",
	    confab.square_brackify_keys('interest',{name: new_name}),
	    function(edited_interest, text_status){
		var updated_interest = edited_interest.pop();
		if(updated_interest!='undefined'&&updated_interest!=null) {
		    $('#interest_edit_'+updated_interest.id).replaceWith(edit_in_place.label_element(updated_interest.id, updated_interest.name));
		} else {

		}
	    }

	);
    }

};

var $RTC_log = {
    _DEBUG : 100,
    _INFO : 75,
    _WARN : 50,
    _ERROR : 0,
    LOG_LEVEL : 100,
    debug_on  :  typeof(console) == 'object' && this.LOG_LEVEL==this._DEBUG,

    /// utility logging methods that use firebug console if found
    debug : function(log_message, obj) {
	if(typeof(console) == 'object' && this.LOG_LEVEL>=this._DEBUG) {
	    typeof(obj)!=='undefined'?console.debug(log_message, obj):console.debug(log_message);
	}
    },
    info : function(log_message, obj) {
	if(typeof(console) == 'object' && this.LOG_LEVEL>=this._INFO) {
	    typeof(obj)!=='undefined'?console.info(log_message, obj):console.info(log_message);
	}
    },
    warn : function(log_message, obj) {
	if(typeof(console) == 'object' && this.LOG_LEVEL>=this._WARN) {
	    typeof(obj)!=='undefined'?console.warn(log_message, obj):console.warn(log_message);
	}
    },
    error : function(log_message, obj) {
	if(typeof(console) == 'object' && this.LOG_LEVEL>=this._ERROR) {
	    typeof(obj)!=='undefined'?console.error(log_message, obj):console.error(log_message);
	}
    }
}